#include "iuCommandRoute.hpp"
#include <cstdio>
#include <cstring>
#include "iuApplication.hpp"
#include "iuInterface.hpp"
#include "iuModuleIP.hpp"
#include "iuRoute.hpp"
#include "iuRouteRule.hpp"

/**
 * @brief Recupere le nom de la commande
 * @return Renvoie le nom de la commande (ici, "route")
 */
const char *iuCommandRoute::getName() const
{
   static const char *name = "route";

   return name;
}

/**
 * @brief Execute la commande
 * @param numArgs le nombre d'arguments de la commande
 * @param args les arguments de la commande (args[0] est le nom de la commande)
 * @param app l'application executant la commande
 */
void iuCommandRoute::execute(int numArgs, char **args, iuApplication &app)
{
   iuRoute &routeTable = app.getModuleIP().getRouteTable();

   //Liste des routes
   if (numArgs == 1)
   {
      routeTable.print();
   }

   //Ajout d'une loi de routage
   else if (numArgs >= 4 && strcmp(args[1], "add") == 0)
   {
      iuRouteRule *rule;
      bool netmaskFound = false;

      rule = new iuRouteRule();
      rule->up = true;
      rule->gate = false;
      rule->gateway.set(0x00000000);
      rule->interface = NULL;

      //Determination host ou network
      if (strcmp(args[2], "-host") == 0)
         rule->host = true;
      else if (strcmp(args[2], "-net") == 0)
         rule->host = false;
      else
      {
         printf("route usage: route add -[host|net]...\n");
         delete rule;
         return;
      }

      //Lecture de l'adresse IP de destination
      if (!rule->dest.fromString(args[3]))
      {
         printf("route: Invalid destination address\n");
         delete rule;
         return;
      }

      //Determination option suivante
      for (int i = 4; i < numArgs - 1; i += 2)
      {
         //Lecture de la passerelle
         if (strcmp(args[i], "gw") == 0)
         {
            rule->gate = true;
            if (!rule->gateway.fromString(args[i+1]))
            {
               printf("route: Invalid gateway address\n");
               delete rule;
               return;
            }
         }

         //Lecture de l'interface
         else if (strcmp(args[i], "dev") == 0)
         {
            int number;

            if (sscanf(args[i+1], "eth%d", &number) != 1)
            {
               printf("route: Invalid interface name\n");
               delete rule;
               return;
            }

            if (!(rule->interface = app.getInterfaceByNumber(number)))
            {
               printf("route: No such interface\n");
               delete rule;
               return;
            }
         }

         //Lecture du masque
         else if (strcmp(args[i], "netmask") == 0)
         {
            netmaskFound = true;
            if (!rule->mask.fromString(args[i+1]))
            {
               printf("route: Invalid netmask address\n");
               delete rule;
               return;
            }
         }

         //Usage incorrect
         else
         {
            printUsage();
            return;
         }
      }

      //Calcule automatiquement le masque
      if (!netmaskFound)
      {
         if (rule->host)
            rule->mask.set(0xffffffff);
         else if (rule->dest.get() == 0x00000000)
            rule->mask.set(0x00000000);
         else if (!(rule->dest.get() & 0x80000000))
            rule->mask.set(0xff000000);
         else if (!(rule->dest.get() & 0x40000000))
            rule->mask.set(0xffff0000);
         else
            rule->mask.set(0xffffff00);
      }

      //Calcule automatiquement l'interface si l'on passe par une passerelle
      if (!rule->interface && rule->gate)
      {
         iuRouteRule *r;

         if ((r = routeTable.route(rule->gateway)))
            rule->interface = r->interface;
      }

      //Ajoute la regle
      if (rule->interface)
         routeTable.addRule(rule);
      else
      {
         printf("route: No interface specified\n");
         delete rule;
      }
   }

   //Suppression d'une loi de routage
   else if (numArgs == 3 && strcmp(args[1], "del") == 0)
   {
      int number;

      if (sscanf(args[2], "%d", &number) != 1)
      {
         printf("route: Invalid argument: the third argument-must be a number\n");
         return;
      }

      routeTable.removeRule(number);
   }

   //Desactivation d'une loi de routage
   else if (numArgs == 3 && strcmp(args[1], "down") == 0)
   {
      int number;

      if (sscanf(args[2], "%d", &number) != 1)
      {
         printf("route: Invalid argument\n");
         printf("route: Invalid argument: the third argument-must be a number\n");
         return;
      }

      routeTable.setDown(number);
   }

   //Activation d'une loi de routage
   else if (numArgs == 3 && strcmp(args[1], "up") == 0)
   {
      int number;

      if (sscanf(args[2], "%d", &number) != 1)
      {
         printf("route: Invalid argument: the third argument-must be a number\n");
         return;
      }

      routeTable.setUp(number);
   }

   //Usage incorrect
   else
   {
      printUsage();
   }
}

/**
 * @brief Affiche le manuel d'utilisation de la commande "route"
 */
void iuCommandRoute::printUsage() const
{
   printf("route: Incorrect usage:\n");
   printf("  route:              Lists the route rules\n");
   printf("  route del n:        Deletes the rule #n\n");
   printf("  route up n:         Activates the rule #n\n");
   printf("  route down n:       Deactivates the rule #n\n");
   printf("  route add -[host|net] 10.0.0.1 [netmask 255.255.0.0] [gw 10.0.0.2] [dev eth0]:\n");
   printf("                      Adds a new route roule\n");
}
